﻿// Implements a flyweight wrapper around the DOM element to allow a higher level manipulation of the DOM
define([
    'Wjs/Wjs'
],
function () {
    'use strict';

    return Wjs.defineOnce({

        type: 'Element',

        internal: { // The members we don't want to get copied to the prototype when creating the type

            construct: function () {

                this.tag = 'div'; // Default HTML tag
            }
        },

        onParentSet: function(fcn) {

            this.parentSetHandler = Wjs.chain(this.parentSetHandler, fcn);

            return this;
        },

        setParent: function (parent) {

            // Verify the parent parameter is a view (container) element
            if (parent && !(parent instanceof Wjs.getType('Element'))) {

                throw 'Parent must be an element';
            }

            this.parent = parent;

            if (this.parentSetHandler) {

                this.parentSetHandler();
            }

            return this;
        },

        getParent: function() {

            return this.parent;
        },

        setId: function (id) {

            this.id = id;

            return this;
        },

        setDomElement: function (domElement) {

            this.domEl = domElement;

            return this;
        },

        getDomElement: function (domElement) {

             return this.domEl;
        },

        setTag: function (tag) {

            this.tag = tag;

            return this;
        },

        // Function to "draw" or render the element from a provided record or data
        setDraw: function (fcn) {

            this.draw = fcn;

            return this;
        },

        onCreate: function (fcn) {

            this.createHandler = fcn;

            return this;
        },
        
        onBeforeRender: function (fcn) {

            this.beforeRenderHandler = Wjs.chain(this.beforeRenderHandler, fcn);

            return this;
        },

        onRender: function (fcn) {

            this.renderHandler = Wjs.chain(this.renderHandler, fcn);

            return this;
        },

        onBeforeDestroy: function (fcn) {

            this.beforeDestroyHandler = fcn;

            return this;
        },

        // Renders this element in the DOM
        render: function (fragment) {
            var htmlType;

            if (this.isRendered()) {

                return;
            }

            if (this.beforeRenderHandler && this.beforeRenderHandler() === false) {

                return;
            }

            // Notify the mixins interested in handling this event
            this.constructor.notifyMixins('onBeforeDomElementCreated', this);

            this.createDomElement(); // Create the DOM element

            // Notify the mixins interested in handling this event
            this.constructor.notifyMixins('onDomElementCreated', this);

            htmlType = Wjs.getType('Html', true); // It is optional since it does not need to be loaded

            if (!htmlType || (this instanceof htmlType === false)) { // In case of Html elments when the DOM element is created, the inner HTML is already added to its parent

                if (fragment) {

                    fragment.appendChild(this.domEl); // Append this element to its parent
                }
                else {

                    if (!this.parent) {

                        throw 'Parent element not set';
                    }

                    this.parent.domEl.appendChild(this.domEl); // Append this element to its parent
                }
            }

            if (this.renderHandler) { // Call the handler to perform extra steps on the element with the DOM created

                this.renderHandler();
            }
        },

        isRendered: function () {

            return this.domEl ? true : false;
        },

        // Creates the DOM element of this element
        createDomElement: function () {
            var el;

            if (this.id) {

                el = document.getElementById(this.id); // Check to see if there is an existing DOM element with that id

                if (!el) { // The DOM element does not exist

                    el = document.createElement(this.tag);

                    el.id = this.id;
                }
            }
            else { // There is no id provided

                el = document.createElement(this.tag);
            }

            this.domEl = el;
        },

        // Retrieves the closest parent element that matches the test
        up: function (test) {
            var parent = this.parent;

            while (parent) {

                if (!(parent instanceof Wjs.getType('View'))) { // Must be a view

                    throw 'Parent must be a view';
                }

                if (test(parent)) {

                    return parent;
                }

                parent = parent.parent;
            }

            return null; // Not found
        },

        // Destroyes the element
        destroy: function () {
            var el,
                id;

            if (this.isDestroying) {

                return; 
            }

            this.isDestroying = true;

            if (this.beforeDestroyHandler) { // Called after the children are destroyed but before this component is destroyed

                this.beforeDestroyHandler();
            }

            // Notify the mixins
            this.constructor.notifyMixins('onDestroy', this);

            el = this.domEl;

            if (this.isRendered()) {

                el.parentNode.removeChild(el); // Remove the element from the DOM
            }

            id = this.id;

            if (id && Wjs.get(id)) { // Remove it from the cache of instances

                Wjs.remove(id);
            }
        },

        // Creates a clone of this element
        clone: function () {
            var cloned = Wjs.new(this.type);

            cloned.parentSetHandler = this.parentSetHandler;

            // cloned.setParent(this.parent); // Do not set the parent of the element since it is meant to be cloned to be used in a different container

            // cloned.setId(this.id); // Do not set the identifier since it is meant to be unique

            // cloned.setDomElement(this.domEl) // Do not set the same DOM element

            cloned.tag = this.tag;

            cloned.draw = this.draw;

            cloned.createHandler = this.createHandler;

            cloned.beforeRenderHandler = this.beforeRenderHandler;

            cloned.renderHandler = this.renderHandler;

            cloned.beforeDestroyHandler = this.beforeDestroyHandler;

            return cloned;
        }

    });

});